package com.mall.common.hibernate.dynamic;

import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.Validate;
import org.dom4j.Document;
import org.dom4j.Element;
import org.hibernate.util.xml.MappingReader;
import org.hibernate.util.xml.OriginImpl;
import org.hibernate.util.xml.XmlDocument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;

public class DefaultDynamicHibernateStatementBuilder implements DynamicHibernateStatementBuilder, ResourceLoaderAware {

	private static final Logger logger = LoggerFactory.getLogger(DefaultDynamicHibernateStatementBuilder.class);
	private Map<String, String> namedHqlQueries;
	private Map<String, String> namedSqlQueries;
	private String[] fileNames = new String[0];
	private ResourceLoader resourceLoader;
	private EntityResolver entityResolver = new DynamicStatementDTDEntityResolver();
	/**
	 * 查询语句名称缓存，不允许重复
	 */
	private Set<String> nameCache = new HashSet<String>();

	@Override
	public Map<String, String> getNamedHQLQueries() {
		return namedHqlQueries;
	}

	@Override
	public Map<String, String> getNamedSQLQueries() {
		return namedSqlQueries;
	}

	@Override
	public void init() throws IOException {
		namedHqlQueries = new HashMap<String, String>();
		namedSqlQueries = new HashMap<String, String>();
		boolean flag = this.resourceLoader instanceof ResourcePatternResolver;
		for (String file : fileNames) {
			if (flag) {
				Resource[] resources = ((ResourcePatternResolver) this.resourceLoader).getResources(file);
				buildMap(resources);
			} else {
				Resource resource = resourceLoader.getResource(file);
				buildMap(resource);
			}
		}
		//clear name cache
		nameCache.clear();

	}

	private void buildMap(Resource[] resources) throws IOException {
		if (resources == null) {
			return;
		}
		for (Resource resource : resources) {
			buildMap(resource);
		}
	}
	
	@SuppressWarnings("rawtypes")
	private void buildMap(Resource resource) {
		InputSource inputSource = null;
		try {
			inputSource = new InputSource(resource.getInputStream());
			
			XmlDocument metadataXml = MappingReader.INSTANCE.readMappingDocument(entityResolver, inputSource,
					new OriginImpl("file", resource.getFilename()));
			if (isDynamicStatementXml(metadataXml)) {
				final Document doc = metadataXml.getDocumentTree();
				final Element dynamicHibernateStatement = doc.getRootElement();
				Iterator rootchildren = dynamicHibernateStatement.elementIterator();
				while (rootchildren.hasNext()) {
					final Element element = (Element) rootchildren.next();
					final String elementname = element.getName();
					if ("sql-query".equals(elementname)) {
						putStatementToCacheMap(resource, element, namedSqlQueries);
					} else if ("hql-query".equals(elementname)) {
						putStatementToCacheMap(resource, element, namedHqlQueries);
					}
				}
			}
		} catch (Exception e) {
			logger.error(e.toString());
			throw new SysException(e);
		} finally {
			if (inputSource != null && inputSource.getByteStream() != null) {
				try {
					inputSource.getByteStream().close();
				} catch (IOException e) {
					logger.error(e.toString());
					throw new SysException(e);
				}
			}
		}
	}
	private void putStatementToCacheMap(Resource resource, final Element element, Map<String, String> statementmap)
			throws IOException {
		String sqlQueryName = element.attribute("name").getText();
		Validate.notEmpty(sqlQueryName);
		if (nameCache.contains(sqlQueryName)) {
			throw new SysException("重复的sql-query/hql-query语句定义在文件:" + resource.getURI() + "中，必须保证name的唯一.");
		}
		nameCache.add(sqlQueryName);
		String querytext = element.getText();
		statementmap.put(sqlQueryName, querytext);
	}
	
	private static boolean isDynamicStatementXml(XmlDocument xmlDocument) {
		return "dynamic-hibernate-statement".equals(xmlDocument.getDocumentTree().getRootElement().getName());
	}

	
	@Override
	public void setResourceLoader(ResourceLoader resourceLoader) {
		this.resourceLoader = resourceLoader;
	}

	public void setFileNames(String[] fileNames) {
		this.fileNames = fileNames;
	}

}
